using System;
using System.Collections.Generic;
using System.Text;

namespace System.Collections
{
    /// <summary>
    /// A nice, dual purpose class that encapsulates both the functionality of a Stack and a Queue
    /// </summary>
    /// <typeparam name="T"></typeparam>
	public class StackQueue<T> : IQueue<T>, IStack<T>
    {
        #region Private Variables
        private System.Collections.Generic.List<T> _ActualList;
        #endregion

        #region Constructors
        /// <summary>
        /// Private constructor that initializes this stack-queue with a populated list of type <typeparamref name="T"/>
        /// </summary>
        /// <param name="actualList">A populated set items for the initial contents of this stack-queue</param>
        private StackQueue(List<T> actualList)
        {
            _ActualList = actualList;
        }

        /// <summary>
        /// Default public contructor for a new instance of a StackQueue
        /// </summary>
        public StackQueue()
            :this(new List<T>())
        {
        }

        /// <summary>
        /// Creates a new instance of StackQueue with an array of <typeparamref name="T"/> for its initial contents
        /// </summary>
        /// <param name="initialContents"></param>
        public StackQueue(T[] initialContents)
            :this(new List<T>(initialContents))
        {
        }

        /// <summary>
        /// Creates a new instance of StackQueue with the given default capacity
        /// </summary>
        /// <param name="capacity">The starting capacity of the StackQueue</param>
        public StackQueue(int capacity)
            :this(new List<T>(capacity))
        {
        }
        #endregion

        #region IQueue<T> Members

        /// <summary>
        /// Retrieve the first-most item in the StackQueue without removing it
        /// </summary>
        /// <returns>Object of type <typeparamref name="T"/></returns>
        public T Peek()
        {
            return _ActualList[0];
        }

        /// <summary>
        /// Adds an item of type <typeparamref name="T"/> to the end of the StackQueue
        /// </summary>
        /// <param name="value"></param>
        public void Enqueue(T value)
        {
            _ActualList.Insert(0, value);
        }

        /// <summary>
        /// Removes the first-most item from the StackQueue and returns it
        /// </summary>
        /// <returns>Object of type <typeparamref name="T"/></returns>
        public T Dequeue()
        {
            T v;
            v = _ActualList[0];
            _ActualList.RemoveAt(0);
            return v;
        }

        /// <summary>
        /// Determines if the given object is contained within the StackQueue
        /// </summary>
        /// <param name="value">The object in question</param>
        /// <returns>Returns <value>true</value> if the object exists within the StackQueue, otherwise <value>false</value></returns>
        public bool Contains(T value)
        {
            return _ActualList.Contains(value);
        }

        #endregion

        #region IQueue Members

        object IQueue.Peek()
        {
            return _ActualList[0];
        }

        void IQueue.Enqueue(object value)
        {
            _ActualList.Insert(0, (T)value);
        }

        object IQueue.Dequeue()
        {
            return this.Pop();
        }

        bool IQueue.Contains(object value)
        {
            return _ActualList.Contains((T)value);
        }
        bool IStack.Contains(object value)
        {
            return _ActualList.Contains((T)value);
        }

        /// <summary>
        /// Clears all items from the StackQueue
        /// </summary>
        public void Clear()
        {
            _ActualList.Clear();
        }

        #endregion

        #region ICollection Members

        /// <summary>
        /// Copies this StackQueue into the given System.Array starting at the given index of that array
        /// </summary>
        /// <param name="array">The array to copy contents into</param>
        /// <param name="index">The starting index of the target array to begin copying data into</param>
        public void CopyTo(Array array, int index)
        {
            ((ICollection)_ActualList).CopyTo(array, index);
        }

        /// <summary>
        /// Gets the number of items in this StackQueue
        /// </summary>
        public int Count
        {
            get { return _ActualList.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether or not access to this StackQueue is synchronized (thread safe)
        /// </summary>
        public bool IsSynchronized
        {
            get { return ((ICollection)_ActualList).IsSynchronized; }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to this StackQueue
        /// </summary>
        public object SyncRoot
        {
            get { return ((ICollection)_ActualList).SyncRoot; }
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through this StackQueue
        /// </summary>
        /// <returns>Returns an IEnumerator affixed to this StackQueue</returns>
        public IEnumerator GetEnumerator()
        {
            return _ActualList.GetEnumerator();
        }

        #endregion

        #region IStack<T> Members

        /// <summary>
        /// Inserts an item at the beginning of this StackQueue
        /// </summary>
        /// <param name="value"></param>
        public void Push(T value)
        {
            _ActualList.Insert(0, value);
        }

        /// <summary>
        /// Gets the first item in this StackQueue, removing it in the process
        /// </summary>
        /// <returns>Returns an instance of <typeparamref name="T"/></returns>
        public T Pop()
        {
            T v;
            v = _ActualList[0];
            _ActualList.RemoveAt(0);
            return v;
        }

        #endregion

        #region IStack Members

        object IStack.Peek()
        {
            return _ActualList[0];
        }

        void IStack.Push(object value)
        {
            _ActualList.Insert(0, (T)value);
        }

        object IStack.Pop()
        {
            object v;
            v = _ActualList[0];
            _ActualList.RemoveAt(0);
            return v;
        }

        #endregion

        public T[] Dequeue(int count)
        {
            System.Collections.Generic.List<T> tl = new System.Collections.Generic.List<T>();
            int xm = this.Count;
            int x = 0;
            for (x = 0; (x < count) && (x < xm); x++)
            {
                tl.Add(this.Dequeue());
            }
            return tl.ToArray();
        }
        object[] IQueue.Dequeue(int count)
        {
            System.Collections.Generic.List<object> tl = new System.Collections.Generic.List<object>();
            int xm = this.Count;
            int x = 0;
            for (x = 0; (x < count) && (x < xm); x++)
            {
                tl.Add((object)this.Dequeue());
            }
            return tl.ToArray();
        }

        public T[] Pop(int count)
        {
            System.Collections.Generic.List<T> tl = new System.Collections.Generic.List<T>();
            int xm = this.Count;
            int x = 0;
            for (x = 0; (x < count) && (x < xm); x++)
            {
                tl.Add(this.Pop());
            }
            return tl.ToArray();
        }
        object[] IStack.Pop(int count)
        {
            System.Collections.Generic.List<object> tl = new System.Collections.Generic.List<object>();
            int xm = this.Count;
            int x = 0;
            for (x = 0; (x < count) && (x < xm); x++)
            {
                tl.Add((object)this.Pop());
            }
            return tl.ToArray();
        }

        public T[] Peek(int count)
        {
            List<T> lResult = new List<T>();
            lResult.AddRange(this.ToArray());
            if (lResult.Count > count)
            {
                lResult.RemoveRange(count, lResult.Count - count);
            }
            return lResult.ToArray();
        }

        public T[] ToArray()
        {
            return _ActualList.ToArray();
        }

        object[] IStack.Peek(int count)
        {
            return ((object[])((System.Array)this.Peek(count)));
        }

        object[] IStack.ToArray()
        {
            return ((object[])((Array)this.ToArray()));
        }

        object[] IQueue.Peek(int count)
        {
            return ((object[])((Array)this.Peek(count)));
        }

        object[] IQueue.ToArray()
        {
            return ((object[])((Array)this.ToArray()));
        }

        #region IQueue<T> Members


        public void Enqueue(T[] values)
        {
            foreach (T v in values)
            {
                this.Enqueue(v);
            }
        }

        #endregion

        #region IQueue Members


        void IQueue.Enqueue(object[] values)
        {
            foreach (object v in values)
            {
                ((IQueue)this).Enqueue(v);
            }
        }

        #endregion
    }
}
